package controller

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"go-gin-api/app/controller"
	"go-gin-api/app/model"
	"go-gin-api/app/model/response"
	"go-gin-api/core/log"
	"go-gin-api/core/middleware"
	"go.uber.org/zap"
	"net/http"
	"net/url"
	"strconv"
	"time"
)

//详细的连接信息
type ConnInfo struct {
	conn     *middleware.Connection
	UserInfo model.UserInfo
}

var (
	//定义 websocket 的转换器
	upgrader = websocket.Upgrader{
		// 握手过程中 如果需要跨域 会允许跨域
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
	}
	ConnPoll []*ConnInfo //连接池

)

type WsController struct {
	controller.Base
	Model string
}

//socket
func (w *WsController) WsHandler(c *gin.Context) {
	var (
		wsConn       *websocket.Conn
		err          error
		conn         *middleware.Connection
		data         []byte
		connPollItem *ConnInfo //用户信息跟socket链接组合
		query        url.Values
		//isNemUser bool = true
	)

	query = c.Request.URL.Query()

	// 连接 webSocket 服务器
	if wsConn, err = upgrader.Upgrade(c.Writer, c.Request, nil); err != nil {
		return
	}
	err = w.MakeContext(c, w.Model).
		MakeOrm(c).Errors
	if err != nil {
		log.Ctx(c).Error("读取参数失败", zap.Error(err))
		w.Error()
		return
	}
	loginName, _ := c.Get("loginName")
	ID, _ := strconv.Atoi(loginName.(string))
	user := model.User{ID: uint64(ID)}
	user.GetUser(w.Orm)
	connPollItem = &ConnInfo{
		nil,
		(&model.UserInfo{}).CreateUserInfo(query, c.ClientIP(), user),
	}

	// 初始化连接 封装底层细节 开放线程安全的API
	if conn, err = middleware.InitConnection(wsConn, connPollItem.UserInfo); err != nil {
		goto ERR
	}
	connPollItem.conn = conn

	ConnPoll = append(ConnPoll, connPollItem)

	// 开启协程心跳
	go heartbeat(connPollItem)

	connPollItem.NewUserAccess() //新用户进入

	//循环 读取 返回消息
	for {
		//从 chan 中读取消息
		if data, err = connPollItem.conn.ReadMessage(); err != nil {
			goto ERR
		}
		//逻辑处理
		doEvent(data, connPollItem.UserInfo)
	}

ERR:
	//TODO 如果出错 就关闭链接
	conn.Close()
}

//心跳协程
func heartbeat(ConnInfo *ConnInfo) {
	var (
		err error
	)
	for {
		msg := response.ChatResult(model.MessageItem{
			MType:   1,
			Message: "heartbeat",
		}, model.UserInfo{})
		if err = ConnInfo.conn.WriteMessage(msg); err != nil {
			fmt.Println("心跳出错：", err)
			ConnInfo.UserLogout() //发送聊天退出的消息
			return
		}
		//每隔1秒 发送一条消息给客户端
		time.Sleep(2 * time.Second)
	}
}

//新的用户进入
func (ConnInfo *ConnInfo) NewUserAccess() {
	olineCount := 0

	for range ConnPoll {
		olineCount = olineCount + 1
	}

	var onlineCount int64
	msg := response.Result2(model.MessageItem{
		MType:   1,
		Message: "login",
	}, ConnInfo.UserInfo, int(onlineCount))
	//通知大家
	fmt.Println("新用户进入：", string(msg))
	broadcast(msg)
}

//用户退出
func (ConnInfo *ConnInfo) UserLogout() {
	//先将自己从聊天池中移除
	fmt.Println(ConnInfo.UserInfo.Nickname + "退出聊天室")
	ConnInfo.Delete()

	onlineCount := 0
	msg := response.Result2(model.MessageItem{
		MType:   1,
		Message: "logout",
	}, ConnInfo.UserInfo, onlineCount)

	//清空token

	//在线状态修改

	//通知大家
	fmt.Println("用户退出：", string(msg))
	broadcast(msg)
}

//移除
func (ConnInfo *ConnInfo) Delete() {
	for k, item := range ConnPoll {
		if item == ConnInfo {
			ConnPoll = append(ConnPoll[:k], ConnPoll[k+1:]...)
			break
		}
	}
}

//程序处理
func doEvent(data []byte, info model.UserInfo) {

	MessageInfo, err := response.Parse(data)
	if err != nil {
		fmt.Println("消息接收错误:", err)
	}
	//1 系统消息  2 群聊消息  3 私聊消息  4 系统操作
	switch MessageInfo.MType {
	case 1: //系统消息
	case 2: //群聊消息
		msg := response.ChatResult(MessageInfo, info)
		broadcast(msg)
	case 3: //私聊消息
		msg := response.ChatResult(MessageInfo, info)
		broadcast(msg)
	case 4: //系统操作
	}

}

//数据广播
func broadcast(msg []byte) {
	if msg == nil {
		fmt.Println("数据广播发送失败")
		return
	}
	//fmt.Println("broadcast" + string(msg))
	for _, connItem := range ConnPoll {
		if err := connItem.conn.WriteMessage(msg); err != nil {
			connItem.conn.Close() //关闭连接
		}
	}
}
